/**
 * edurender
 * er_math.h
 * (c) 2009 J. Brad Byrd
 */

#ifndef ER_MATH_H
#define ER_MATH_H

#include "er_types.h"
#include "er_macros.h"

#include <math.h>

ER_FORCE_INLINE ERVector3f *erAdd3(ERVector3f *out, const ERVector3f *a, const ERVector3f *b)
{
	out->x = a->x + b->x;
	out->y = a->y + b->y;
	out->z = a->z + b->z;
	
	return out;
}

ER_FORCE_INLINE ERVector3f *erSub3(ERVector3f *out, const ERVector3f *a, const ERVector3f *b)
{
	out->x = a->x - b->x;
	out->y = a->y - b->y;
	out->z = a->z - b->z;
	
	return out;
}

ER_FORCE_INLINE ERVector4f *erLerp4(ERVector4f *out, const ERVector4f *a, const ERVector4f *b, ERfloat alpha)
{
	ERfloat ialpha = 1.0f - alpha;
	ERVector4f result;
	
	result.x = ialpha * a->x + alpha * b->x;
	result.y = ialpha * a->y + alpha * b->y;
	result.z = ialpha * a->z + alpha * b->z;
	result.w = ialpha * a->w + alpha * b->w;
	
	*out = result;
	
	return out;	
}

ER_FORCE_INLINE ERVector3f *erLerp3(ERVector3f *out, const ERVector3f *a, const ERVector3f *b, ERfloat alpha)
{
	ERfloat ialpha = 1.0f - alpha;
	ERVector3f result;
	
	result.x = ialpha * a->x + alpha * b->x;
	result.y = ialpha * a->y + alpha * b->y;
	result.z = ialpha * a->z + alpha * b->z;
	
	*out = result;
	
	return out;	
}

ER_FORCE_INLINE ERint erMin2i(ERint a, ERint b)
{
	return a < b ? a : b;
}

ER_FORCE_INLINE ERint erMin3i(ERint a, ERint b, ERint c)
{
	return erMin2i(erMin2i(a, b), c);
}

ER_FORCE_INLINE ERint erMin4i(ERint a, ERint b, ERint c, ERint d)
{
	return erMin2i(erMin3i(a, b, c), d);
}

ER_FORCE_INLINE ERint erMax2i(ERint a, ERint b)
{
	return a > b ? a : b;
}

ER_FORCE_INLINE ERint erMax3i(ERint a, ERint b, ERint c)
{
	return erMax2i(erMax2i(a, b), c);
}

ER_FORCE_INLINE ERint erMax4i(ERint a, ERint b, ERint c, ERint d)
{
	return erMax2i(erMax3i(a, b, c), d);
}

ER_FORCE_INLINE ERfloat erMin2f(ERfloat a, ERfloat b)
{
	return a < b ? a : b;
}

ER_FORCE_INLINE ERfloat erMin3f(ERfloat a, ERfloat b, ERfloat c)
{
	return erMin2f(erMin2f(a, b), c);
}

ER_FORCE_INLINE ERfloat erMax2f(ERfloat a, ERfloat b)
{
	return a > b ? a : b;
}

ER_FORCE_INLINE ERfloat erMax3f(ERfloat a, ERfloat b, ERfloat c)
{
	return erMax2f(erMax2f(a, b), c);
}

ER_FORCE_INLINE ERint erRound(ERfloat value)
{
	// JBB_TODO: This should really be done w/ SSE to avoid rounding mode junk.
	return (ERint)(value + 0.5f);
}

ER_FORCE_INLINE float erDot3(const ERVector3f *a, const ERVector3f *b)
{
	return a->x * b->x + a->y * b->y + a->z * b->z;
};

ER_FORCE_INLINE float erDot4(const ERVector4f *a, const ERVector4f *b)
{
	return a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w;
};

ER_FORCE_INLINE ERVector3f *erNormalize3(ERVector3f *out, const ERVector3f *v)
{
	ERfloat invMag = 1.0f / sqrtf(erDot3(v, v));
	
	out->x = v->x * invMag;
	out->y = v->y * invMag;
	out->z = v->z * invMag;

	return out;
}

ER_FORCE_INLINE ERVector3f *erCross3(ERVector3f *out, const ERVector3f *a, const ERVector3f *b)
{
	ERfloat x = a->y * b->z - a->z * b->y;
	ERfloat y = a->z * b->x - a->x * b->z;
	ERfloat z = a->x * b->y - a->y * b->x;
	
	out->x = x;
	out->y = y;
	out->z = z;

	return out;
}

ER_FORCE_INLINE ERfloat erSaturate(ERfloat x)
{
	return (x < 0.0) ? 0.0f : ((x > 1.0f) ? 1.0f : x);
}

ERMatrix4x4f *erIdentityMatrix(ERMatrix4x4f *out);
ERMatrix4x4f *erMulMatrix(ERMatrix4x4f *out, const ERMatrix4x4f *a, const ERMatrix4x4f *b);
ERMatrix4x4f *erTransposeMatrix(ERMatrix4x4f *out, const ERMatrix4x4f *m);
ERVector3f *erTransformProjectPoint3(ERVector3f *out, const ERMatrix4x4f *m, const ERVector3f *p);
ERVector4f *erTransformPoint3(ERVector4f *out, const ERMatrix4x4f *m, const ERVector3f *p);

#endif // ER_MATH_H
